#include "L_System.h"

Expression :: Expression()
{
	oper = -1;
	operand = 0;
	conditionVal = 0;

	secondary = 0;
	constant = true;
}

Expression :: Expression(int op, int opera, int sec, float condVal, bool cons)
{
	oper = op;
	operand = opera;
	conditionVal = condVal;
	secondary  = sec;
	constant = cons;
}

Module :: Module()
{
	symbol = 0;
	parameters = std::vector<float>();
	expressions = std::vector<Expression>();
}

Module :: Module(char sym)
{
	symbol = sym;
	parameters = std::vector<float>();
	expressions = std::vector<Expression>();
}

//For use in turtle-interpreted lists, input to production application. Creates modules with real-valued parameters.
Module :: Module(char sym, std::vector<float> params)
{
	symbol = sym;
	parameters = params;
	expressions = std::vector<Expression>();
}

//For use in productions successors, creates template modules with expression-valued parameters.
Module :: Module(char sym, std::vector<Expression> newParams)
{
	symbol = sym;
	parameters = std::vector<float>();
	expressions = newParams;
}

Production :: Production()
{
	symbol = 0;
	numParameters = 0;
	condition.operand = 0;

	condition.conditionVal = 0;
	condition.oper = 0;

	successors = std::vector<Module>();
}

Production :: Production(char sym, int nProd, Expression exp, std::vector<Module> suc)
{
	symbol = sym;
	numParameters = nProd;
	condition = exp;
	successors = suc;
}

bool Production :: checkCondition(Module module)
{
	//A production matches a module when:
	//i. The symbol in the module and the production are the same
	if(symbol == module.symbol)
	{
		//ii. The number of parameters in the module is equal to the number in the production predecessor
		if(numParameters == module.parameters.size())
		{
			float moduleValue;
			//Get the parameter value based on the parameter position specified in the condition Expression
			if(!module.parameters.empty())
				moduleValue = module.parameters[condition.operand];
			//iii. The condition evaluates to true using the module parameter - These are logical operations only
			switch(condition.oper)
			{
				case 0:
					if(moduleValue == condition.conditionVal)
						return true;
					break;
				case 1:
					if(moduleValue > condition.conditionVal)
						return true;
					break;
				case 2:
					if(moduleValue < condition.conditionVal)
						return true;
					break;
				case 3:
					if(moduleValue >= condition.conditionVal)
						return true;
					break;
				case 4:
					if(moduleValue <= condition.conditionVal)
						return true;
					break;
				default:			//No defined operator means no condition, so the production is always applied, and hopefully nobody pops an arithmetic operator in there
					return true;
			}
		}
	}
	return false;
}

std::vector<Module> Production::applyProduction(Module module)
{
	std::vector<Module> newModules;	//We will append this vector to the build vector during propogation
	Module m;		
	for(auto i : successors)		//Loop through each module in the successor
	{
		m.parameters = i.parameters;
		for(auto j : i.expressions)	//Loop through each expression in the module
		{
			float newValue;
			if(j.constant)						//Differentiate between constant-valued expressions and 
				newValue = j.conditionVal;		//those involving two parameters
			else
				newValue = module.parameters[j.secondary];

			switch(j.oper)	//The switch will determine the correct arithmetic operator to use, as well 
			{								//as if the module under scrutiny is expressionless (default)
				case 5:
					newValue = module.parameters[j.operand] + newValue;		//To pass a parameter through as in A(s) -> F(s)
					m.parameters.push_back(newValue);						//the expression should equal A(s) -> F(s + 0)
					break;
				case 6:
					newValue = module.parameters[j.operand] - newValue;
					m.parameters.push_back(newValue);
					break;
				case 7:
					newValue = module.parameters[j.operand] * newValue;
					m.parameters.push_back(newValue);
					break;
				case 8:
					newValue = module.parameters[j.operand] / newValue;
					m.parameters.push_back(newValue);
					break;
				default:		//No operator defined, this module does not take or deal with parameters, the new list should not have any
					m.parameters = std::vector<float>();
			}
		}
		
		m.symbol = i.symbol;
		newModules.push_back(m);
	}
	return newModules;
}